--[[
1. set / get server to / from shared dict
2. 对 server 进行统计 管理

  ngx.shared.upserver.servers = 服务列表
  ngx.shared.upserver.lbpolicy = 负载均衡策略
  ngx.shared.upserver.statistic = 服务统计

--]]

local upserver = ngx.shared.upserver
local mp = require ('MessagePack')
local random = require('math').random
local randomseed = require('math').randomseed
local table = table
local os = os

local function _log(str) ngx.log(ngx.ERR, str) end
local function _dummyLog(str) end

local _M = {}
setfenv(1,_M)

_M.log = _log
_M.dummyLog = _dummyLog


local function _getId(aServer) -- ip:port
  return aServer
end
_M.getId = _getId

function _M.parseId(id)
  local ip, port = string.match(id,'(.-):(.*)')
  return ip, port
end

--[[
设置服务器列表, 服务器列表的格式是:
servers={
  type1={ip:port, ip:port, ip:port},
  type2={ip:port, ip:port, ip:port},
  type3={ip:port, ip:port, ip:port},
}
id=md5(ip:port)
--]]

function _M.setServers(servers)
  upserver:set('servers', mp.pack(servers))
end

local function _getServers(typeName)
  local servers = upserver:get('servers')
  if not servers then
    _log("servers nil")
    return nil
  end
  
  servers = mp.unpack(servers)
  if not typeName then return servers end
  return servers[typeName]

end
_M.getServers = _getServers

--[[

--]]

function _M.selectOneServer(typeName)
  if not typeName then _log ("selectOneServer nil typeName") return nil end
  local list = _getServers(typeName)
  if not list then  _log("selectOneServer list nil") return nil   end

  local len = table.getn(list)
  -- _log("len[" ..typeName .. "]=" .. len)
  randomseed(os.time())
  local index = random(len)
  _log("select " .. index .. " in " .. len)
  local targetServer = list[random(len)]
  return targetServer
end

--[[
读取服务器状态
statistic={
  id={
    state=ok/error/close, 
    succ=num, 
    fail=num, 
    pending=num,
    mtcost=123, ms
    mturl=/foo/bar
  }
}
--]]
_M.SERVER_STATE = {
  ok=1,
  error=2,
  disable=3
}

local function _getStatistic()
  local statistic = upserver:get('statistic')
  if not statistic then return nil end
  statistic = mp.unpack(statistic)
  return statistic
end
_M.getStatistic = _getStatistic

local function _setStatistic(statistic)
  upserver:set('statistic',mp.pack(statistic))
end
_M.setStatistic = _setStatistic

function _M.changeState(aServer, state)
  local statistic = _getStatistic()
  if not statistic then return nil end
  local id = _getId(aServer)
  local server = statistic[id]
  if not server then return nil end
  local preState = server.state

  if server.state ~= state then
    server.state = state
    -- write back
    upserver:set('statistic',mp.pack(statistic))
    return true
  end
  return false
end

local _RESPONSE_STATE = {
  succ = 1,
  fail = 2
}

--[[
  TODO
--]]
local function _updateStatus(aServer, url, timeCost, respCode)
end

local function _incRespState (aServer, respState)
  local statistic = _getStatistic()
  if not statistic then statistic = {} end

  local id = _getId(aServer)
  local server = statistic[id]
  if not server then
    _log("_incRespState " .. aServer .. " statistic not exists")
    server = {state=1, succ=0, fail=0, pending=0}
    statistic[id] = server
  end

  if _RESPONSE_STATE.succ == respState then
    server.succ = server.succ + 1
  elseif _RESPONSE_STATE.fail == respState then
    server.fail = server.fail + 1
  else
    _log("unknown respState = " .. respState)
    return
  end

  -- write back
  upserver:set('statistic',mp.pack(statistic))
end

-- bug: 多个worker并行时，不能保证数据一致性
function _M.incSucc(aServer)  -- server="ip:port"
  _log("incSucc " .. aServer )
  _incRespState(aServer, _RESPONSE_STATE.succ)
end

return _M



